/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.kramerius.imports;

import cz.mzk.rehan.commons.DatastreamId;
import cz.mzk.rehan.commons.Namespaces;
import java.util.Map;
import java.util.HashMap;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;

/**
 *
 * @author Martin Rehanek (rehan@mzk.cz)
 */
public abstract class Foxml {

    private static final String PID_PREFIX = "uuid:";
    private final String uuid;
    private final String pid;
    private final String label;
    private Document document;
    //TODO: odstranit elementy
    private Element dcXmlContent;
    private Element modsXmlContent;
    private Element relsExtXmlContent;
    private Element policyContentLocation;
    protected Element rootElement;
    private static final Boolean datastreamsVersionable = true;
    private static final int MAX_LABEL_LENGTH = 100;
    private static final String OVER_MAX_LENGTH_SUFFIX = "...";
    private final Map<DatastreamId, Datastream> datastreamMap = new HashMap<DatastreamId, Datastream>();

    public Foxml(String uuid, String label) {
        this.uuid = uuid;
        this.pid = PID_PREFIX.concat(uuid);
        this.label = trim(label, MAX_LABEL_LENGTH);
        createDocument();
    }

    private String trim(String originalString, int maxLength) {
        if (originalString.length() <= maxLength) {
            return originalString;
        } else {
            return originalString.substring(0, maxLength - OVER_MAX_LENGTH_SUFFIX.length())
                    + OVER_MAX_LENGTH_SUFFIX;
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Foxml other = (Foxml) obj;
        if ((this.uuid == null) ? (other.uuid != null) : !this.uuid.equals(other.uuid)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 43 * hash + (this.uuid != null ? this.uuid.hashCode() : 0);
        return hash;
    }

    private void createDocument() {
        createDocumentAndRootElement();
        createProperties();
        //dcXmlContent = createDcXmlContent();
//        modsXmlContent = createModsXmlContent();
//        relsExtXmlContent = createRelsExtXmlContent();
//        policyContentLocation = createPolicyContentLocation();
    }

    private void createDocumentAndRootElement() {
        rootElement = DocumentHelper.createElement(new QName("digitalObject", Namespaces.foxml));
        document = DocumentHelper.createDocument(rootElement);
        document.getRootElement().add(Namespaces.foxml);
        document.getRootElement().add(Namespaces.xsi);
        document.getRootElement().addAttribute(new QName("schemaLocation", Namespaces.xsi), "info:fedora/fedora-system:def/foxml# http://www.fedora.info/definitions/1/0/foxml1-1.xsd");
        rootElement.addAttribute("VERSION", "1.1");
        rootElement.addAttribute("PID", pid);
    }

//    private Element createDcXmlContent() {
//        Element dataStream = createDatastreamElement(rootElement, "A", "X", "DC");
//        Element dataStreamVersion = createDatastreamVersionElement(dataStream, "http://www.openarchives.org/OAI/2.0/oai_dc/", "text/xml", "Dublin Core Record for this object", "DC.0");
//        Element xmlContent = dataStreamVersion.addElement(new QName("xmlContent", Namespaces.foxml));
//        return xmlContent;
//    }
    //TODO: predelat takto vsechny datastreamy
//    private Element createModsXmlContent() {
//        Element dataStream = appendDatastream(rootElement, "A", "X", "BIBLIO_MODS");
//        Element dataStreamVersion = createDatastreamVersionElement(dataStream, null, "text/xml", "BIBLIO_MODS description of current object", "BIBLIO_MODS.0");
//        Element xmlContent = dataStreamVersion.addElement(new QName("xmlContent", Namespaces.foxml));
//        return xmlContent;
//    }
//    private Element createRelsExtXmlContent() {
//        Element dataStream = appendDatastream(rootElement, "A", "X", "RELS-EXT");
//        Element dataStreamVersion = createDatastreamVersionElement(dataStream, "info:fedora/fedora-system:FedoraRELSExt-1.0", "application/rdf+xml", "RDF Statements about this object", "RELS-EXT.0");
//        Element xmlContent = dataStreamVersion.addElement(new QName("xmlContent", Namespaces.foxml));
//        return xmlContent;
//    }
//    private Element createPolicyContentLocation() {
//        Element dataStream = appendDatastream(rootElement, "A", "E", "POLICY");
//        Element dataStreamVersion = createDatastreamVersionElement(dataStream, null, "text/xml", null, "POLICY.0");
//        Element contentLocation = dataStreamVersion.addElement(new QName("contentLocation", Namespaces.foxml));
//        return contentLocation;
//    }
    public void addDatastream(Datastream ds) {
        if (datastreamMap.containsKey(ds.getId())) {
            throw new RuntimeException("Datastream with id " + ds.getId() + " already present");
        }
        datastreamMap.put(ds.getId(), ds);
    }

    /**
     * add datastream to element getModsContent()
     */
    public void addModsDatastream(Document modsDoc) {
        modsXmlContent.add(modsDoc.getRootElement().detach());
    }

    public void addRelsExtDatastream(Document relsExtDoc) {
        relsExtXmlContent.add(relsExtDoc.getRootElement().detach());
    }

    public void addPolicyDatastream(Policy policy) {
        policyContentLocation.addAttribute("TYPE", "URL");
        switch (policy) {
            case PRIVATE:
                policyContentLocation.addAttribute("REF", "http://local.fedora.server/fedora/get/policy:private/POLICYDEF");
            case PUBLIC:
                policyContentLocation.addAttribute("REF", "http://local.fedora.server/fedora/get/policy:public/POLICYDEF");
        }
    }

    private void createProperties() {
        Element properties = rootElement.addElement(new QName("objectProperties", Namespaces.foxml));
        addProperty(properties, "info:fedora/fedora-system:def/model#label", label);
        addProperty(properties, "info:fedora/fedora-system:def/model#state", "A");
        addProperty(properties, "info:fedora/fedora-system:def/model#ownerId", "fedoraAdmin");
    }

    private void addProperty(Element properties, String name, String value) {
        Element property = properties.addElement(new QName("property", Namespaces.foxml));
        property.addAttribute("NAME", name);
        property.addAttribute("VALUE", value);
    }

    public final String getPid() {
        return pid;
    }

    public final String getUuid() {
        return uuid;
    }

    public String getLabel() {
        return label;
    }

    public final Document getFoxmlWithDatastreams() {
        Document copy = (Document) document.clone();
        Document withDatastreams = appendDatastreams(copy);
        return withDatastreams;
    }

    private Document appendDatastreams(Document doc) {
        for (Datastream ds : datastreamMap.values()) {
            appendDatastream(ds, doc);
        }
        return doc;
    }

    private void appendDatastream(Datastream ds, Document document) {
        Element datastreamEl = createDatastreamElement(document.getRootElement(), "A", ds.getControlGroup(), ds.getId());
        String versionId = ds.getId().toString() + ".0";
        Element dataStreamVersion = createDatastreamVersionElement(datastreamEl, ds.getFormatUri(), ds.getMimeType(), ds.getLabel(), versionId);
        switch (ds.getControlGroup()) {
            case X:
                addXmlContent((InlineXmlDs) ds, dataStreamVersion);
                break;
            case E:
                addContentLocation((ExternallyReferencedDs) ds, dataStreamVersion);
                break;
        }
    }

    protected Element createDatastreamElement(Element rootEl, String state, ControlGroup controlGroup, DatastreamId dsId) {
        Element dataStream = rootEl.addElement(new QName("datastream", Namespaces.foxml));
        dataStream.addAttribute("VERSIONABLE", datastreamsVersionable.toString());
        dataStream.addAttribute("STATE", state);
        dataStream.addAttribute("CONTROL_GROUP", controlGroup.name());
        dataStream.addAttribute("ID", dsId.toString());
        return dataStream;
    }

    protected Element createDatastreamVersionElement(Element rootEl, String formatUri, String mimetype, String label, String id) {
        Element dataStreamVersion = rootEl.addElement(new QName("datastreamVersion", Namespaces.foxml));
        if (formatUri != null) {
            dataStreamVersion.addAttribute("FORMAT_URI", formatUri);
        }
        dataStreamVersion.addAttribute("MIMETYPE", mimetype);
        //probably will be generated
        //dataStreamVersion.addAttribute("CREATED", getDateTime());
        if (label != null) {
            dataStreamVersion.addAttribute("LABEL", label);
        }
        dataStreamVersion.addAttribute("ID", id);
        return dataStreamVersion;
    }

    private void addXmlContent(InlineXmlDs ds, Element dataStreamVersion) {
        Element xmlContent = dataStreamVersion.addElement(new QName("xmlContent", Namespaces.foxml));
        //dataStreamVersion.add(xmlContent);
        //dataStreamVersion.addElement("xmlContent");

        xmlContent.add(ds.getContent().getRootElement().detach());
    }

    private void addContentLocation(ExternallyReferencedDs ds, Element dataStreamVersion) {
        Element contentLocation = dataStreamVersion.addElement(new QName("contentLocation", Namespaces.foxml));
        contentLocation.addAttribute("TYPE", ds.getReferenceType().name());
        contentLocation.addAttribute("REF", ds.getReference());
    }
}
